/*
 * SoapUI, Copyright (C) 2004-2017 SmartBear Software
 *
 * Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent 
 * versions of the EUPL (the "Licence"); 
 * You may not use this work except in compliance with the Licence. 
 * You may obtain a copy of the Licence at: 
 * 
 * http://ec.europa.eu/idabc/eupl 
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the Licence is 
 * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
 * express or implied. See the Licence for the specific language governing permissions and limitations 
 * under the Licence. 
 */

package com.eviware.soapui.impl.wsdl.panels.testcase;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.SoapUISystemProperties;
import com.eviware.soapui.analytics.Analytics;
import com.eviware.soapui.analytics.SoapUIActions;
import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
import com.eviware.soapui.impl.wsdl.support.HelpUrls;
import com.eviware.soapui.impl.wsdl.testcase.WsdlTestCase;
import com.eviware.soapui.support.Tools;
import com.eviware.soapui.support.UISupport;
import com.eviware.soapui.support.components.JXToolBar;
import com.eviware.soapui.support.components.WebViewBasedBrowserComponent;
import com.eviware.soapui.support.components.WebViewBasedBrowserComponentFactory;
import com.eviware.soapui.testondemand.DependencyValidator;
import com.eviware.soapui.testondemand.Location;
import com.eviware.soapui.testondemand.TestOnDemandCaller;
import com.eviware.x.dialogs.Worker.WorkerAdapter;
import com.eviware.x.dialogs.XProgressDialog;
import com.eviware.x.dialogs.XProgressMonitor;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;

import javax.annotation.Nonnull;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

/**
 * Panel for displaying a Test On Demand report
 */
public class TestOnDemandPanel extends JPanel {
    // FIXME This should be a URL on our servers. Replace with the real URL when it has been developed by our web dev team.
    private static final String FIRST_PAGE_URL = "http://www.soapui.org/Appindex/test-on-demand.html";

    private static final String GET_MORE_LOCATIONS_URL = "http://www2.smartbear.com/AlertSite_Monitor_APIs_Learn_More.html";
    private static final String GET_MORE_LOCATIONS_MESSAGE = "More locations trial";

    private static final String INITIALIZING_MESSAGE = "Initializing...";

    private static final String NO_LOCATIONS_FOUND_MESSAGE = "No locations found";

    private static final String COULD_NOT_GET_LOCATIONS_MESSAGE = "Could not get Test On Demand Locations. Check your network connection.";
    private static final String COULD_NOT_UPLOAD_MESSAGE = "Could not upload TestCase to the selected location";

    private static final String UPLOAD_TEST_CASE_HEADING = "Upload TestCase";
    private static final String UPLOADING_TEST_CASE_MESSAGE = "Uploading TestCase..";

    private static final String SERVER_IP_ADDRESSES_PREFIX = "IP: ";
    private static final String SERVER_IP_ADDRESSES_DELIMETER = ", ";
    private static final String NO_SERVER_IP_ADDRESSES_MESSAGE = "<No IP addresses found>";

    // FIXME This suggest using a Java 7 feature, Fix compiler level!
    @Nonnull
    private JComboBox locationsComboBox;

    private WebViewBasedBrowserComponent browser;

    @Nonnull
    private Action sendTestCaseAction;

    @Nonnull
    private static List<Location> locationsCache = new ArrayList<Location>();

    @Nonnull
    JLabel serverIPAddressesLabel = new JLabel();

    @Nonnull
    TestOnDemandCaller caller;

    private final WsdlTestCase testCase;

    protected DependencyValidator validator;

    public TestOnDemandPanel(WsdlTestCase testCase) {
        super(new BorderLayout());

        this.testCase = testCase;
        setBackground(Color.WHITE);
        setOpaque(true);

        setValidator();

        setCaller();

        add(buildToolbar(), BorderLayout.NORTH);
    }

    protected void setValidator() {
        validator = new DependencyValidator();
    }

    protected void setCaller() {
        caller = new TestOnDemandCaller();
    }

    public void release() {
        if (browser != null) {
            browser.close(true);
        }
    }

    private Component buildToolbar() {
        JXToolBar toolbar = UISupport.createToolbar();

        sendTestCaseAction = new SendTestCaseAction();
        sendTestCaseAction.setEnabled(false);

        locationsComboBox = buildInitializingLocationsComboBox();
        locationsComboBox.addActionListener(new LocationComboBoxAction());

        // FIXME Is there a way to make this fill a the rest of the X space?
        serverIPAddressesLabel.setPreferredSize(new Dimension(1000, 10));
        serverIPAddressesLabel.setForeground(Color.GRAY);

        toolbar.addFixed(UISupport.createToolbarButton(sendTestCaseAction));
        toolbar.addRelatedGap();
        toolbar.addFixed(locationsComboBox);
        toolbar.addSpace(10);
        toolbar.addFixed(serverIPAddressesLabel);
        toolbar.addGlue();
        toolbar.addFixed(UISupport.createToolbarButton(new ShowOnlineHelpAction(HelpUrls.ALERT_SITE_HELP_URL)));

        return toolbar;
    }

    private JComboBox buildInitializingLocationsComboBox() {
        JComboBox initLocationsComboBox = new JComboBox();
        // FIXME This should be dynamic
        initLocationsComboBox.setPreferredSize(new Dimension(170, 10));
        initLocationsComboBox.addItem(INITIALIZING_MESSAGE);
        initLocationsComboBox.setEnabled(false);
        return initLocationsComboBox;
    }

    public void initializeLocationsCache() {
        if (locationsCache.isEmpty()) {
            new TestOnDemandCallerThread().start();
        } else {
            populateLocationsComboBox();
        }
    }

    private void populateLocationsComboBox() {
        locationsComboBox.removeAllItems();

        if (locationsCache.isEmpty()) {
            locationsComboBox.addItem(NO_LOCATIONS_FOUND_MESSAGE);
            openInInternalBrowser(SoapUI.STARTER_PAGE_ERROR_URL);
        } else {
            for (Location location : locationsCache) {
                locationsComboBox.addItem(location);
            }

            locationsComboBox.addItem(GET_MORE_LOCATIONS_MESSAGE);

            locationsComboBox.setEnabled(true);
            sendTestCaseAction.setEnabled(true);
        }

        invalidate();
    }

    private void openInInternalBrowser(String url) {
        if (!SoapUI.isBrowserDisabled()) {
            browser.navigate(url);
        }
    }

    private void openInExternalBrowser(String url) {
        Tools.openURL(url);
    }

    private String getFirstPageURL() {
        return System.getProperty(SoapUISystemProperties.TEST_ON_DEMAND_FIRST_PAGE_URL, FIRST_PAGE_URL);
    }

    private String getMoreLocationsURL() {
        return System.getProperty(SoapUISystemProperties.TEST_ON_DEMAND_GET_LOCATIONS_URL, GET_MORE_LOCATIONS_URL);
    }

    public void ensureBrowserIsInitialized() {

        if (browser == null) {
            browser = WebViewBasedBrowserComponentFactory.createBrowserComponent(false);
            add(browser.getComponent(), BorderLayout.CENTER);

            openInInternalBrowser(FIRST_PAGE_URL);
        }

    }

    private class SendTestCaseAction extends AbstractAction {
        public SendTestCaseAction() {
            putValue(SMALL_ICON, UISupport.createImageIcon("/run.png"));
            putValue(Action.SHORT_DESCRIPTION, "Run Test On Demand report");
        }

        public void actionPerformed(ActionEvent arg0) {


            if (validator != null && !validator.isValid(testCase)) {
                UISupport.showErrorMessage("Your project contains external dependencies that "
                        + "are not supported by the Test-On-Demand functionality at this point.");
                return;
            }

            if (locationsComboBox != null) {
                Location selectedLocation = (Location) locationsComboBox.getSelectedItem();

                XProgressDialog progressDialog = UISupport.getDialogs().createProgressDialog(UPLOAD_TEST_CASE_HEADING, 3,
                        UPLOADING_TEST_CASE_MESSAGE, false);
                SendTestCaseWorker sendTestCaseWorker = new SendTestCaseWorker(testCase, selectedLocation);
                try {
                    progressDialog.run(sendTestCaseWorker);
                } catch (Exception e) {
                    SoapUI.logError(e);
                }

                String redirectUrl = sendTestCaseWorker.getResult();
                if (!Strings.isNullOrEmpty(redirectUrl)) {
                    openURLSafely(redirectUrl);
                }

                Analytics.trackAction(SoapUIActions.TEST_ON_DEMAND);
            }
        }
    }

    private void openURLSafely(String url)

    {
        if (SoapUI.isBrowserDisabled()) {
            Tools.openURL(url);
        } else {
            if (browser != null) {
                browser.navigate(url);
            }
        }
    }

    private class LocationComboBoxAction implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            Object selectedItem = locationsComboBox.getSelectedItem();

            if (selectedItem != null) {
                if (locationsComboBox.getSelectedItem().equals(GET_MORE_LOCATIONS_MESSAGE)) {
                    openInExternalBrowser(getMoreLocationsURL());
                    sendTestCaseAction.setEnabled(false);
                    serverIPAddressesLabel.setText(null);
                } else {
                    if (locationsComboBox.isEnabled() && !sendTestCaseAction.isEnabled()) {
                        openInInternalBrowser(getFirstPageURL());
                        sendTestCaseAction.setEnabled(true);
                    }

                    if (selectedItem instanceof Location) {
                        String[] serverIPAddresses = ((Location) selectedItem).getServerIPAddresses();

                        if (serverIPAddresses != null && serverIPAddresses.length > 0) {
                            String severIpAddressList = Joiner.on(SERVER_IP_ADDRESSES_DELIMETER).join(serverIPAddresses);
                            serverIPAddressesLabel.setText(SERVER_IP_ADDRESSES_PREFIX + severIpAddressList);
                        } else {
                            serverIPAddressesLabel.setText(SERVER_IP_ADDRESSES_PREFIX + NO_SERVER_IP_ADDRESSES_MESSAGE);
                            // FIXME: Log errors aswell?
                        }
                        invalidate();
                    }
                }
            }
        }
    }

    private class SendTestCaseWorker extends WorkerAdapter {
        private final WsdlTestCase testCase;
        private final Location selectedLocation;
        private String result = null;

        public SendTestCaseWorker(WsdlTestCase testCase, Location selectedLocation) {
            this.testCase = testCase;
            this.selectedLocation = selectedLocation;
        }

        public Object construct(XProgressMonitor monitor) {
            try {
                result = caller.sendTestCase(testCase, selectedLocation);
            } catch (Exception e) {
                SoapUI.logError(e);
                UISupport.showErrorMessage(COULD_NOT_UPLOAD_MESSAGE);
            }
            return result;
        }

        public String getResult() {
            return result;
        }
    }

    // Used to prevent soapUI from halting while waiting for the Test On Demand server to respond
    private class TestOnDemandCallerThread extends Thread {
        @Override
        public void run() {
            try {
                locationsCache = caller.getLocations();
            } catch (Exception e) {
                SoapUI.logError(e, COULD_NOT_GET_LOCATIONS_MESSAGE);
            } finally {
                populateLocationsComboBox();
            }
        }
    }
}
